package com.googlecode.animator.view.topmenu
{
	import com.googlecode.animator.data.Notifications;
	import com.googlecode.animator.data.ProjectConstants;
	import com.googlecode.animator.data.ViewportVO;
	import com.googlecode.animator.model.ProjectSettingsProxy;
	
	import flash.events.Event;
	import flash.filesystem.File;
	import flash.net.FileFilter;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	
	import mx.controls.Alert;
	import mx.events.CloseEvent;
	import mx.events.MenuEvent;
	
	import org.puremvc.as3.interfaces.INotification;
	import org.puremvc.as3.patterns.mediator.Mediator;
	
	/**
	 * Integrates the TopMenu view component with the PureMVC framework.
	 * 
	 * Note: Instances of this Mediator may be dynamically created and removed by the application. 
	 * @author Martin
	 */
	public class TopMenuMediator1 extends Mediator
	{
		/**
		 * PureMVC identifier. 
		 */
		public static const NAME:String = "TopMenuMediator";
		
		/**
		 * Remembers if a new project is to be opened. 
		 */
		private var _newProjectAfterSaving:Boolean = false;
		
		/**
		 * Remembers if a project is to be opened from XML. 
		 */
		private var _openProjectAfterSaving:Boolean = false;
		
		/**
		 * remembers the filepath to the XML selected by the user to open the project. 
		 */
		private var _openProjectFilepath:String = null;
		
		private var _defaultXML:XML = null;
		
		private var _projectXML:XML = null;
		
		/**
		 * Constructor. 
		 * @param viewComponent
		 * 
		 */
		public function TopMenuMediator1(viewComponent:TopMenu)
		{
			super(NAME, viewComponent);
		}
		
		//////////////////////////////////////////////////////////////////////
		//
		// PureMVC
		//
		//////////////////////////////////////////////////////////////////////
		
		/**
		 * Sets up event listeners to handle menu interactions. 
		 * 
		 */
		override public function onRegister():void
		{
			viewRef.mainMenu.addEventListener(MenuEvent.CHANGE, onMenuItemClick);
		}
		
		/**
		 * Clean up. 
		 * 
		 */
		override public function onRemove():void
		{
			viewRef.mainMenu.removeEventListener(MenuEvent.CHANGE, onMenuItemClick);
		}
		
		/**
		 * Register interest in notifications. 
		 * @return 
		 * 
		 */
		override public function listNotificationInterests():Array
		{
			var list:Array = super.listNotificationInterests();
			
			list.push(Notifications.PROJECT_SAVED);
			list.push(Notifications.PROJECT_IS_RESET);
			//list.push(Notifications.DEFAULT_VIEWPORTS_ADDED);
			
			return list;
		}
		
		/**
		 * Handle notifications sent by PureMVC. 
		 * @param notification
		 * 
		 */
		override public function handleNotification(notification:INotification):void
		{
			super.handleNotification(notification);
			
			switch(notification.getName())
			{
				case Notifications.PROJECT_SAVED:
					
					handleProjectSaved();
					
					break;
				
				case Notifications.PROJECT_IS_RESET:
					
					handleProjectReset();
					
					break;
					
				case Notifications.DEFAULT_VIEWPORTS_ADDED:
					
					handleDefaultViewportsAdded();
					
					break;
				
				case Notifications.DEFAULT_CAMERAS_ADDED:
					
					//sendNotification(Notifications.ADD_CAMERA_PROXIES);
					
					break;
				case Notifications.CAMERA_PROXIES_ADDED:
					
					//sendNotification(Notifications.VIEWPORT_SETUP_COMPLETE);
					
					break;
			}
		}
		
		//////////////////////////////////////////////////////////////////////
		//
		// Event handlers
		//
		//////////////////////////////////////////////////////////////////////
		
		/**
		 * Handles selection of menu items. 
		 * @param evt
		 * 
		 */
		protected function onMenuItemClick(evt:MenuEvent):void
		{
			switch(String(evt.item.@data))
			{
				case "New":
					
					newProjectSelected();
					
					break;
				
				case "Save":
					
					saveProjectSelected();
					
					break;
				
				case "Save as":
					
					saveProjectSelected(true);
					
					break;
				
				case "Open":
					
					openProjectSelected();
					
					break;
			}
		}
		
		/**
		 * Responds to the New Project main menu item being clicked.
		 * 
		 */
		protected function newProjectSelected():void
		{
			// protect against the user accidently destroying all data
			Alert.show("Do you want to save project?", "Warning!", Alert.YES | Alert.NO | Alert.CANCEL, null, onNewProjectAlert);
		}
		
		/**
		 * Attempting to open a new project causes an alert box to pop up. This  is
		 * because all data will be destroyed. The user is asked whether 
		 * or not to save the project. 
		 * @param evt
		 * 
		 */
		protected function onNewProjectAlert(evt:CloseEvent):void
		{
			switch(evt.detail)
			{
				case Alert.YES:
				
					// the user has requested that the project be saved
					_newProjectAfterSaving = true;
					saveProjectSelected();
					
					break;
				
				case Alert.NO:
					
					// the user would like to go ahead and create new project
					_newProjectAfterSaving = true;
					openNewProject();
					
					break;
				
				case Alert.CANCEL:
					
					// do nothing.
					
					break;
			}
		}
		
		/**
		 * Initiates the process of starting a new project. First the project is reset. Then we
		 * wait for notification of that fact. Finally, we rebuild the project. 
		 * 
		 */
		protected function openNewProject():void
		{
			sendNotification(Notifications.RESET_PROJECT);
		}
		
		/**
		 * Calling this function opens a file browser in preparation for
		 * saving the project.
		 * 
		 * @param saveAs
		 * 
		 */
		protected function saveProjectSelected(saveAs:Boolean = false):void
		{
			var file:File;
			
			// if no save path has been set, go and get one
			if(settings.savePath == null || saveAs == true)
			{
				file = new File();
				
				try
				{
					file.addEventListener(Event.SELECT, onSaveSelect);
					file.addEventListener(Event.CANCEL, onSaveCancel);
					file.browseForSave("Save Project");
				}
				catch (error:Error)
				{
    				trace("Failed:", error.message);
				}	
			}else
			{
				sendNotification(Notifications.SAVE_PROJECT);
			}
			
		}
		
		/**
		 * Responds to the user selecting a file path with which to save the project. 
		 * @param evt
		 * 
		 */
		protected function onSaveSelect(evt:Event):void
		{
			(evt.target as File).removeEventListener(Event.SELECT, onSaveSelect);
			(evt.target as File).removeEventListener(Event.CANCEL, onSaveCancel);
			
			sendNotification(Notifications.SAVE_PROJECT, (evt.target as File).url);
		}
		
		/**
		 * Responds to the user cancelling a file save operation. 
		 * @param evt
		 * 
		 */
		protected function onSaveCancel(evt:Event):void
		{	
			(evt.target as File).removeEventListener(Event.SELECT, onSaveSelect);
			(evt.target as File).removeEventListener(Event.CANCEL, onSaveCancel);
			
			_newProjectAfterSaving = false;
			_openProjectAfterSaving = false;
		}
		
		/**
		 * Responds to the user selecting the Open Project main menu item. 
		 * 
		 */
		protected function openProjectSelected():void
		{
			Alert.show("Do you want to save project?", "Warning!", Alert.YES | Alert.NO | Alert.CANCEL, null, onOpenProjectAlert);
		}
		
		/**
		 * Attempting to open a project causes an alert box to pop up. This  is
		 * because all data will be destroyed. The user is asked whether 
		 * or not to save the project. 
		 * @param evt
		 * 
		 */
		protected function onOpenProjectAlert(evt:CloseEvent):void
		{	
			switch(evt.detail)
			{
				case Alert.YES:

					_openProjectAfterSaving = true;
					saveProjectSelected();
					
					break;
				
				case Alert.NO:
					
					_openProjectAfterSaving = true;
					openProject();
					
					break;
				
				case Alert.CANCEL:
					
					// do nothing.
					
					break;
			}

		}
		
		/**
		 * Calling this function opens a file browser in order to select
		 * the file to open.
		 * 
		 * @param saveAs
		 * 
		 */
		protected function openProject():void
		{
			var file:File = new File();
			var ff:FileFilter = new FileFilter("Project file", "*.xml;");
			
			try
			{
				file.addEventListener(Event.SELECT, onOpenSelect);
				file.addEventListener(Event.CANCEL, onOpenCancel);
				file.browseForOpen("Open Project", [ff]);
			}
			catch (error:Error)
			{
				trace("Failed:", error.message);
			}  
		}
		
		/**
		 * Responds to the user selecting a file path with which to open an existing project. 
		 * @param evt
		 * 
		 */
		protected function onOpenSelect(evt:Event):void
		{
			(evt.target as File).removeEventListener(Event.SELECT, onOpenSelect);
			(evt.target as File).removeEventListener(Event.CANCEL, onOpenCancel);
			
			_openProjectFilepath = (evt.target as File).url;
			
			sendNotification(Notifications.RESET_PROJECT);

		}
		
		/**
		 * Responds to the user cancelling a file open operation. 
		 * @param evt
		 * 
		 */
		protected function onOpenCancel(evt:Event):void
		{
			(evt.target as File).removeEventListener(Event.SELECT, onOpenSelect);
			(evt.target as File).addEventListener(Event.CANCEL, onOpenCancel);
		}
		
		/**
		 * Responds to the default XML of the project being downloaded. 
		 * @param evt
		 * 
		 */
		protected function onXMLLoadedAfterReset(evt:Event):void
		{
			var projectXML:XML = checkXML(evt);

			if(projectXML == null)
			{
				return;
			}

			if(_newProjectAfterSaving)
			{
				// the following notifications result in the viewports being rebuilt
				sendNotification(Notifications.ADD_DEFAULT_VIEWPORTS, projectXML);		
				sendNotification(Notifications.ADD_DEFAULT_CAMERAS, projectXML);
				//sendNotification(Notifications.VIEWPORT_SETUP_COMPLETE);
				sendNotification(Notifications.ADD_CAMERA_PROXIES, projectXML);
				_newProjectAfterSaving = false;
			}
			
			if(_openProjectAfterSaving)
			{
				_defaultXML = projectXML;
				var path:String = _openProjectFilepath;
				// load the default XML for the project
				var loader:URLLoader = new URLLoader();
				loader.addEventListener(Event.COMPLETE, onProjectXMLLoaded);
		
				var url:URLRequest = new URLRequest(path);
				loader.load(url);
				
			}

		}
		
		protected function onProjectXMLLoaded(evt:Event):void
		{
			var projectXML:XML = checkXML(evt);

			if(projectXML == null)
			{
				return;
			}
			
			_projectXML = projectXML;
			
			sendNotification(Notifications.ADD_DEFAULT_VIEWPORTS, _defaultXML);	
				
			//sendNotification(Notifications.ADD_DEFAULT_CAMERAS, _defaultXML);
			//sendNotification(Notifications.VIEWPORT_SETUP_COMPLETE);
			//sendNotification(Notifications.ADD_CAMERA_PROXIES, projectXML);
			
			//_defaultXML = null;
			//_openProjectAfterSaving = false;
		}
		
		protected function checkXML(evt:Event):XML
		{
			var defaultXML:XML;
			
			try
			{
				defaultXML = new XML((evt.target as URLLoader).data);
			}
			catch (error:Error)
			{
				Alert.show(error.message, "Sorry!", Alert.OK, null);
				return null;
			}
			
			if(defaultXML.@id != ProjectConstants.PROJECT_NAME)
			{
				Alert.show("This is not a valid file.", "Warning!", Alert.OK, null);
				return null;
			}
			
			return defaultXML;
		}
		//////////////////////////////////////////////////////////////////////
		//
		// PureMVC: Handlers
		//
		//////////////////////////////////////////////////////////////////////
		
		/**
		 * Responds to the project being saved. 
		 * 
		 */
		protected function handleProjectSaved():void
		{
			// the project may have been saved and further action be required
			// in this case a new project is required
			if(_newProjectAfterSaving)
			{
				openNewProject();
				sendNotification(Notifications.FILE_SAVE_PATH, null);
			}
			
			if(_openProjectAfterSaving)
			{
				openProject();
			}
		}
		
		/**
		 * Responds to the project being reset. 
		 * 
		 */
		protected function handleProjectReset():void
		{
			var path:String = ProjectConstants.DEFAULT_PROJECT_PATH;

			/* if(_newProjectAfterSaving)
			{
				path = ProjectConstants.DEFAULT_PROJECT_PATH;
			
			}else if(_openProjectAfterSaving)
			{
				path = _openProjectFilepath;
				
			}else
			{
				return;
			} */
			// load the default XML for the project
			var loader:URLLoader = new URLLoader();
			loader.addEventListener(Event.COMPLETE, onXMLLoadedAfterReset);
		
			var url:URLRequest = new URLRequest(path);
			loader.load(url);
		}
		
		protected function handleDefaultViewportsAdded():void
		{	
			var vVO:ViewportVO;
			
			if(_openProjectAfterSaving)
			{	
				// link in each viewport with correct camera
				for each(var viewport:XML in _projectXML.viewports.viewport)
				{
					vVO = new ViewportVO();
					vVO.viewportID = viewport.@id;
					vVO.currentCamera = viewport.currentCamera;
					
					//settings.updateViewport();
					sendNotification(Notifications.VIEWPORT_UPDATED, vVO);
				}
				
				sendNotification(Notifications.ADD_DEFAULT_CAMERAS, _projectXML);
				sendNotification(Notifications.ADD_CAMERA_PROXIES, _projectXML);
				/* var path:String = _openProjectFilepath;
				// load the default XML for the project
				var loader:URLLoader = new URLLoader();
				loader.addEventListener(Event.COMPLETE, onXMLLoadedAfterViewportsAdded);
		
				var url:URLRequest = new URLRequest(path);
				loader.load(url); */
			} 
			_openProjectAfterSaving = false;
		}
		
		/////////////////////////////////////////////////////////////////
		// Utility Functions
		/////////////////////////////////////////////////////////////////
		
		protected function get viewRef():TopMenu
		{
			return viewComponent as TopMenu;
		}
		
		protected function get settings():ProjectSettingsProxy
		{
			return facade.retrieveProxy(ProjectSettingsProxy.NAME) as ProjectSettingsProxy;
		}
	}
}